package com.wdb.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.template.Template;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.TemplateUtil;
import com.wdb.domain.ColumnInfo;
import com.wdb.domain.GenConfig;
import com.wdb.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: wendaobai@qq.com
 * @date: 2024/9/24 星期二 14:15
 */
@Slf4j
public class GenUtil {
  private static final String TIMESTAMP = "Timestamp";

  private static final String BIGDECIMAL = "BigDecimal";

  public static final String PK = "PRI";

  public static final String EXTRA = "auto_increment";

  public static void generatorCode(List<ColumnInfo> columnInfos, GenConfig genConfig)
      throws IOException {
    Map<String, Object> genMap = getGenMap(columnInfos, genConfig);
    // log.info("genmap--{}",genMap);
    TemplateEngine engine =
        TemplateUtil.createEngine(
            new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
    // 生成后端代码
    List<String> templateNames = getAdminTemplateNames();
    for (String templateName : templateNames) {
      Template template = engine.getTemplate("admin/" + templateName + ".ftl");
      String root = System.getProperty("user.dir");
      String filePath =
          getAdminFilePath(templateName, genConfig, genMap.get("className").toString(), root);
      log.info("filepath--{}", filePath);
      File file = new File(filePath);
      if (!genConfig.getCover() && FileUtil.exist(file)) {
        continue;
      }
      // 生成代码
      genFile(file, template, genMap);
    }
  }

  private static void genFile(File file, Template template, Map<String, Object> genMap)
      throws IOException {
    Writer writer = null;
    // 创建目录
    try {
      FileUtil.touch(file);
      template.render(genMap, file);
    } catch (IORuntimeException e) {
      throw new RuntimeException(e);
    } finally {
      // writer.close();
    }
  }

  /**
   * 根据模板名称、生成配置和类名获取管理员文件的路径
   *
   * @param templateName 模板名称，如"Entity"、"Controller"等
   * @param genConfig 生成配置对象，包含模块名、包名等信息
   * @param className 生成的类名
   * @param rootPath 项目根路径
   * @return 根据模板名称不同返回相应的文件路径，如果模板名称不匹配则返回null
   */
  private static String getAdminFilePath(
      String templateName, GenConfig genConfig, String className, String rootPath) {
    // 拼接项目路径
    String projectPath = rootPath + File.separator + genConfig.getModuleName();
    // 拼接包路径
    String packagePath =
        projectPath
            + File.separator
            + "src"
            + File.separator
            + "main"
            + File.separator
            + "java"
            + File.separator;
    // mybatis配置文件路径
    String mpXmlPath =
        projectPath
            + File.separator
            + "src"
            + File.separator
            + "main"
            + File.separator
            + "resources"
            + File.separator;

    // 如果生成配置中的包名信息非空，则拼接到包路径中
    if (!ObjectUtils.isEmpty(genConfig.getPack())) {
      packagePath += genConfig.getPack().replace(".", File.separator) + File.separator;
    }

    // 根据模板名称分别处理并返回对应的文件路径
    if ("Entity".equals(templateName)) {
      return packagePath + "domain" + File.separator + className + ".java";
    }

    if ("Controller".equals(templateName)) {
      return packagePath + "rest" + File.separator + className + "Controller.java";
    }

    if ("Service".equals(templateName)) {
      return packagePath + "service" + File.separator + className + "Service.java";
    }

    if ("ServiceImpl".equals(templateName)) {
      return packagePath
          + "service"
          + File.separator
          + "impl"
          + File.separator
          + className
          + "ServiceImpl.java";
    }

    if ("QueryCriteria".equals(templateName)) {
      return packagePath
          + "domain"
          + File.separator
          + "vo"
          + File.separator
          + className
          + "QueryCriteria.java";
    }

    if ("Mapper".equals(templateName)) {
      return packagePath + "mapper" + File.separator + className + "Mapper.java";
    }

    if ("Mapper-xml".equals(templateName)) {
      return mpXmlPath + "mapper" + File.separator + className + "Mapper.xml";
    }

    // 如果模板名称不匹配任何已知类型，则返回null
    return null;
  }

  // 获取模版数据
  private static Map<String, Object> getGenMap(List<ColumnInfo> columnInfos, GenConfig genConfig) {
    HashMap<String, Object> genMap = new HashMap<>();
    // 接口别名
    genMap.put("apiAlias", genConfig.getApiAlias());
    // 包名称
    genMap.put("package", genConfig.getPack());
    // 模块名称
    genMap.put("moduleName", genConfig.getModuleName());
    // 作者
    genMap.put("author", genConfig.getAuthor());
    // 创建日期
    genMap.put("date", LocalDate.now().toString());
    // 表名
    genMap.put("tableName", genConfig.getTableName());
    // 大写开头的类名
    String className = StringUtils.toCapitalizeCamelCase(genConfig.getTableName());
    // 小写开头的类名
    String changeClassName = StringUtils.toCamelCase(genConfig.getTableName());
    // 判断是否去除表前缀
    if (StringUtils.isNotEmpty(genConfig.getPrefix())) {
      className =
          StringUtils.toCapitalizeCamelCase(
              StrUtil.removePrefix(genConfig.getTableName(), genConfig.getPrefix()));
      changeClassName =
          StringUtils.toCamelCase(
              StrUtil.removePrefix(genConfig.getTableName(), genConfig.getPrefix()));
      changeClassName = StringUtils.uncapitalize(changeClassName);
    }
    // 保存类名
    genMap.put("className", className);
    // 保存小写开头的类名
    genMap.put("changeClassName", changeClassName);
    // 存在 Timestamp 字段
    genMap.put("hasTimestamp", false);
    // 查询类中存在 Timestamp 字段
    genMap.put("queryHasTimestamp", false);
    // 存在 BigDecimal 字段
    genMap.put("hasBigDecimal", false);
    // 查询类中存在 BigDecimal 字段
    genMap.put("queryHasBigDecimal", false);
    // 是否需要创建查询
    genMap.put("hasQuery", false);
    // 自增主键
    genMap.put("auto", false);
    // 存在字典
    genMap.put("hasDict", false);
    // 存在日期注解
    genMap.put("hasDateAnnotation", false);
    // 存储主键字段名
    genMap.put("pkIdName", "none");
    // 存储符号
    genMap.put("symbol", "#");
    // 保存字段信息
    List<Map<String, Object>> columns = new ArrayList<>();
    // 保存查询字段的信息
    List<Map<String, Object>> queryColumns = new ArrayList<>();
    // 存储字典信息
    List<String> dicts = new ArrayList<>();
    // 存储 between 信息
    List<Map<String, Object>> betweens = new ArrayList<>();
    // 存储不为空的字段信息
    List<Map<String, Object>> isNotNullColumns = new ArrayList<>();

    for (ColumnInfo column : columnInfos) {
      Map<String, Object> listMap = new HashMap<>(16);
      // 字段描述
      listMap.put("remark", column.getRemark());
      // 字段类型
      listMap.put("columnKey", column.getKeyType());
      // 主键类型
      String colType = ColUtil.cloToJava(column.getColumnType());
      // 小写开头的字段名
      String changeColumnName = StringUtils.toCamelCase(column.getColumnName());
      // 大写开头的字段名
      String capitalColumnName = StringUtils.toCapitalizeCamelCase(column.getColumnName());
      if (PK.equals(column.getKeyType())) {
        // 存储主键类型
        genMap.put("pkColumnType", colType);
        // 存储小写开头的字段名
        genMap.put("pkChangeColName", changeColumnName);
        // 存储大写开头的字段名
        genMap.put("pkCapitalColName", capitalColumnName);
        // 存储主键字段名
        genMap.put("pkIdName", column.getColumnName());
      }
      // 是否存在 Timestamp 类型的字段
      if (TIMESTAMP.equals(colType)) {
        genMap.put("hasTimestamp", true);
      }
      // 是否存在 BigDecimal 类型的字段
      if (BIGDECIMAL.equals(colType)) {
        genMap.put("hasBigDecimal", true);
      }
      // 主键是否自增
      if (EXTRA.equals(column.getExtra())) {
        genMap.put("auto", true);
      }
      // 主键存在字典
      if (StringUtils.isNotBlank(column.getDictName())) {
        genMap.put("hasDict", true);
        if (!dicts.contains(column.getDictName())) dicts.add(column.getDictName());
      }

      // 存储字段类型
      listMap.put("columnType", colType);
      // 存储字原始段名称
      listMap.put("columnName", column.getColumnName());
      // 不为空
      listMap.put("istNotNull", column.getNotNull());
      // 字段列表显示
      listMap.put("columnShow", column.getListShow());
      // 表单显示
      listMap.put("formShow", column.getFormShow());
      // 表单组件类型
      listMap.put(
          "formType",
          StringUtils.isNotBlank(column.getFormType()) ? column.getFormType() : "Input");
      // 小写开头的字段名称
      listMap.put("changeColumnName", changeColumnName);
      // 大写开头的字段名称
      listMap.put("capitalColumnName", capitalColumnName);
      // 字典名称
      listMap.put("dictName", column.getDictName());
      // 添加非空字段信息
      if (column.getNotNull()) {
        isNotNullColumns.add(listMap);
      }
      // 判断是否有查询，如有则把查询的字段set进columnQuery
      if (!StringUtils.isBlank(column.getQueryType())) {
        // 查询类型
        listMap.put("queryType", column.getQueryType());
        // 是否存在查询
        genMap.put("hasQuery", true);
        if (TIMESTAMP.equals(colType)) {
          // 查询中存储 Timestamp 类型
          genMap.put("queryHasTimestamp", true);
        }
        if (BIGDECIMAL.equals(colType)) {
          // 查询中存储 BigDecimal 类型
          genMap.put("queryHasBigDecimal", true);
        }
        if ("between".equalsIgnoreCase(column.getQueryType())) {
          betweens.add(listMap);
        } else {
          // 添加到查询列表中
          queryColumns.add(listMap);
        }
      }
      // 添加到字段列表中
      columns.add(listMap);
    }
    // 保存字段列表
    genMap.put("columns", columns);
    // 保存查询列表
    genMap.put("queryColumns", queryColumns);
    // 保存字段列表
    genMap.put("dicts", dicts);
    // 保存查询列表
    genMap.put("betweens", betweens);
    // 保存非空字段信息
    genMap.put("isNotNullColumns", isNotNullColumns);
    return genMap;
  }

  /**
   * 获取后端代码模板名称
   *
   * @return List
   */
  private static List<String> getAdminTemplateNames() {
    List<String> templateNames = new ArrayList<>();
    templateNames.add("Entity");
    templateNames.add("Controller");
    templateNames.add("QueryCriteria");
    templateNames.add("Service");
    templateNames.add("ServiceImpl");
    templateNames.add("Mapper");
    templateNames.add("Mapper-xml");
    return templateNames;
  }

  public static List<Map<String, Object>> preview(List<ColumnInfo> columns, GenConfig genConfig) {
    Map<String, Object> genMap = getGenMap(columns, genConfig);
    List<Map<String, Object>> genList = new ArrayList<>();
    // 获取后端模版
    List<String> templates = getAdminTemplateNames();
    TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
    for (String templateName : templates) {
      Map<String, Object> map = new HashMap<>(1);
      Template template = engine.getTemplate("admin/" + templateName + ".ftl");
      map.put("content", template.render(genMap));
      map.put("name", templateName.replace("-xml", ".xml"));
      genList.add(map);
    }
    return genList;
  }
}
